'''
@create_time: 2025/10/24
@Author: GeChao  
@File: dingtalk_robot.py
@Description: 钉钉群机器人消息推送工具
'''
import requests
import json
import time
import hmac
import hashlib
import base64
import urllib.parse


class DingTalkRobot:
    """钉钉群机器人
    
    使用方法:
        1. 在钉钉群中添加自定义机器人
        2. 获取 Webhook 地址和加签密钥(secret)
        3. 创建实例: robot = DingTalkRobot(webhook, secret)
        4. 发送消息: robot.send_markdown(title, text)
    """
    
    def __init__(self, webhook: str, secret: str = None):
        """
        初始化钉钉机器人
        
        Args:
            webhook: 钉钉机器人的Webhook地址
            secret: 加签密钥(可选,如果机器人设置了加签则必填)
        """
        self.webhook = webhook
        self.secret = secret
    
    def _get_sign_url(self):
        """
        生成带签名的URL(如果设置了secret)
        Returns:
            完整的请求URL
        """
        if not self.secret:
            return self.webhook
        
        # 获取当前时间戳(毫秒)
        timestamp = str(round(time.time() * 1000))
        
        # 拼接字符串
        string_to_sign = f'{timestamp}\n{self.secret}'
        
        # 使用HmacSHA256算法计算签名
        hmac_code = hmac.new(
            self.secret.encode('utf-8'),
            string_to_sign.encode('utf-8'),
            digestmod=hashlib.sha256
        ).digest()
        
        # Base64编码
        sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
        
        # 拼接URL
        signed_url = f'{self.webhook}&timestamp={timestamp}&sign={sign}'
        return signed_url
    
    def send_text(self, content: str, at_mobiles: list = None, is_at_all: bool = False):
        """
        发送文本消息
        
        Args:
            content: 文本内容
            at_mobiles: @的手机号列表
            is_at_all: 是否@所有人
            
        Returns:
            响应结果字典
        """
        data = {
            "msgtype": "text",
            "text": {
                "content": content
            },
            "at": {
                "atMobiles": at_mobiles or [],
                "isAtAll": is_at_all
            }
        }
        
        return self._send_request(data)
    
    def send_markdown(self, title: str, text: str, at_mobiles: list = None, is_at_all: bool = False):
        """
        发送Markdown消息
        
        Args:
            title: 消息标题
            text: Markdown格式的内容
            at_mobiles: @的手机号列表
            is_at_all: 是否@所有人
            
        Returns:
            响应结果字典
        """
        data = {
            "msgtype": "markdown",
            "markdown": {
                "title": title,
                "text": text
            },
            "at": {
                "atMobiles": at_mobiles or [],
                "isAtAll": is_at_all
            }
        }
        
        return self._send_request(data)
    
    def send_link(self, title: str, text: str, message_url: str, pic_url: str = None):
        """
        发送链接消息
        
        Args:
            title: 消息标题
            text: 消息内容(会自动换行显示)
            message_url: 点击消息跳转的URL
            pic_url: 图片URL(可选)
            
        Returns:
            响应结果字典
        """
        data = {
            "msgtype": "link",
            "link": {
                "title": title,
                "text": text,
                "messageUrl": message_url,
                "picUrl": pic_url or ""
            }
        }
        
        return self._send_request(data)
    
    def send_action_card(self, title: str, text: str, 
                        single_title: str = None, single_url: str = None,
                        btn_orientation: str = "0", buttons: list = None):
        """
        发送ActionCard消息(卡片消息)
        
        Args:
            title: 标题
            text: Markdown格式的内容
            single_title: 单个按钮的标题
            single_url: 单个按钮的跳转链接
            btn_orientation: 按钮排列方向("0":竖直, "1":横向)
            buttons: 多个按钮列表 [{"title": "按钮1", "actionURL": "url1"}, ...]
            
        Returns:
            响应结果字典
        """
        data = {
            "msgtype": "actionCard",
            "actionCard": {
                "title": title,
                "text": text,
                "btnOrientation": btn_orientation
            }
        }
        
        if single_title and single_url:
            # 单个按钮模式
            data["actionCard"]["singleTitle"] = single_title
            data["actionCard"]["singleURL"] = single_url
        elif buttons:
            # 多个按钮模式
            data["actionCard"]["btns"] = buttons
        
        return self._send_request(data)
    
    def _send_request(self, data: dict):
        """
        发送HTTP请求
        Args:
            data: 请求数据
        Returns:
            响应结果字典
        """
        try:
            url = self._get_sign_url()
            headers = {'Content-Type': 'application/json'}
            
            response = requests.post(
                url=url,
                headers=headers,
                data=json.dumps(data),
                timeout=10
            )
            
            result = response.json()
            if result.get('errcode') == 0:
                print(f"✅ 钉钉消息发送成功")
                return result
            else:
                print(f"❌ 钉钉消息发送失败: {result.get('errmsg')}")
                return result
                
        except Exception as e:
            print(f"❌ 钉钉消息发送异常: {e}")
            return {"errcode": -1, "errmsg": str(e)}


if __name__ == '__main__':
    # 示例用法
    webhook = "https://oapi.dingtalk.com/robot/send?access_token=df3a7ed4bf950e9815106554dce9458b4e9c1d9b79b5ab9d0ad281cc2682a26a"
    secret = "SEC312c7f0f5aed20d4afc4cd0e8f2693e5d963b00911e6baaddf6f56ab54771e84"  # 如果机器人开启了加签,填写密钥
    
    robot = DingTalkRobot(webhook, secret)
    
    # 发送文本消息
    robot.send_text("这是一条测试消息", is_at_all=False)
    
    # 发送Markdown消息
    markdown_text = """
    #### 杭州天气 @150XXXXXXXX
    > 9度,西北风1级,空气良89,相对温度73%
    > ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)
    > ###### 10点20分发布 [天气](https://www.dingtalk.com)
    """
    robot.send_markdown("杭州天气", markdown_text)
    

